package com.antcom.antread.common.core.baseRest.dao;


import com.antcom.antread.common.core.baseRest.domain.PageDevice;
import com.antcom.antread.common.core.utils.date.DateUtils;
import com.antcom.antread.common.core.utils.string.StringUtils;
import org.hibernate.Session;
import org.hibernate.query.NativeQuery;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.persistence.EntityManager;
import java.io.BufferedReader;
import java.io.Reader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.sql.Clob;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * jpa: dao
 *
 * @Author 中国蚂蚁技术有限公司
 * @create 2020/11/29 12:15
 */
public class EntityManagerDao {

    /**
     * 日志
     */
    protected Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 正则过滤
     */
    protected Pattern pat = Pattern.compile(":[0-9a-zA-z|-|_|.]*");

    @Inject
    protected EntityManager entityManager;

    /**
     * 查询集合
     *
     * @param sql   要执行的sql字符串
     * @param param 查询的参数
     * @param clazz 返回的类型
     * @param <T>   返回的类型泛型
     * @return 返回参数类型数据的集合
     */
    protected <T> List<T> queryList(String sql, Object param, Class<T> clazz) {
        Map<String, Object> map = new HashMap<String, Object>();
        if (param != null) {
            if (param instanceof Map) {
                map = (Map<String, Object>) param;
            } else {
                if (param.getClass() instanceof Class<?>) {
                    getParamMap(sql, map, param);
                }
            }
        }
        List<T> list = queryResult(sql, map, 0, 0, clazz);
        return list;
    }

    /**
     * 分页查询
     *
     * @param sql   要执行的sql字符串
     * @param param 查询的参数
     * @param page  分页的信息
     * @param clazz 返回的类型
     * @param <T>   返回的类型泛型
     */
    protected <T> void queryPageList(String sql, Object param, PageDevice page, Class<T> clazz) {
        Map<String, Object> map = new HashMap<String, Object>();
        if (param != null) {
            if (param instanceof Map) {
                map = (Map<String, Object>) param;
            } else {
                if (param.getClass() instanceof Class<?>) {
                    //把实体类转换为map
                    getParamMap(sql, map, param);
                }
            }
        }
        //查询总记录数
        Integer count = queryCount(sql, map);
        page.setPageTotal(count);
        //查询一页的数据信息集合
        List<T> list = queryResult(sql, map, page.getPageNum() - 1, page.getPageTotal(), clazz);
        if (list != null && list.size() > 0) {
            page.setList(list);
        }
    }

    /**
     * 查询单条
     *
     * @param sql   要执行的sql字符串
     * @param param 查询的参数
     * @param clazz 返回的类型
     * @param <T>   返回的类型泛型
     * @return 参数返回类型的数据信息
     */
    protected <T> T queryOne(String sql, Object param, Class<T> clazz) {
        Map<String, Object> map = new HashMap<String, Object>();
        if (param != null) {
            if (param instanceof Map) {
                map = (Map<String, Object>) param;
            } else {
                if (param.getClass() instanceof Class<?>) {
                    //实体类转换为map
                    getParamMap(sql, map, param);
                }
            }
        }
        //查询符合条件的一条数据
        List<T> list = queryResult(sql, map, 0, 1, clazz);
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    /**
     * 分页查询条数
     *
     * @param sql 要执行的sql字符串
     * @param map 查询条件
     * @return 符合条件的总记录数
     */
    protected Integer queryCount(String sql, Map<String, Object> map) {
        String countSql = String.format("select count(1) from (%s) tmp_count", sql);
        NativeQuery query = createQuery(countSql, map);
        Object obj = query.getSingleResult();
        if (obj != null) {
            return StringUtils.toInteger(obj);
        }
        return 0;
    }

    /**
     * 查询集合
     *
     * @param sql         要执行的sql字符串
     * @param param       查询条件
     * @param firstResult 符合条件数据集合截取的开始位置
     * @param maxResults  符合条件数据集合截取的截止位置
     * @param clazz       返回的类型
     * @param <T>         返回的类型泛型
     * @return 符合条件的从开始位置到截止位置的数据集合
     */
    protected <T> List<T> queryResult(String sql, Map<String, Object> param, int firstResult, int maxResults,
                                      Class<T> clazz) {
        NativeQuery query = createQuery(sql, param);
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        if (firstResult > 0) {
            //设置截取的开始位置
            query.setFirstResult(firstResult);
        }
        if (maxResults > 0) {
            //设置截取的截止位置
            query.setMaxResults(maxResults);
        }
        entityManager.close();
        List<Map<String, Object>> list = query.getResultList();

        logger.info(String.format("SQL为:%s", sql));
        logger.info(String.format("参数为:%s", param));

        if (list != null && list.size() > 0) {
            if (clazz == Map.class) {
                return (List<T>) list;
            } else if (clazz == String.class || clazz == Integer.class || clazz == Long.class || clazz == Double.class
                    || clazz == Float.class || clazz == Date.class) {
                //数据类型转换
                return mapToBasicType(clazz, list);
            } else {
                //集合中的数据转换
                return getlist(list, clazz);
            }
        }
        return null;
    }

    /**
     * 拼接条件参数
     *
     * @param sql   要执行的sql字符串
     * @param param 查询的参数
     * @return 设置好参数的查询hibernate查询sql
     */
    private NativeQuery createQuery(String sql, Map<String, Object> param) {
        NativeQuery query = createNativeQuery(sql);
        if (param != null) {
            for (Map.Entry<String, Object> entry : param.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return query;
    }

    /**
     * 创建sql
     *
     * @param sql 要执行的sql字符串
     * @return 转换为hibernate的查询sql
     */
    private NativeQuery createNativeQuery(String sql) {
        Session session = entityManager.unwrap(Session.class);
        return session.createNativeQuery(sql);
    }

    /**
     * 把map集合转换为某种数据类型的集合
     *
     * @param clazz         要转换的数据类型
     * @param resultMapList 要转换的数据map集合
     * @param <T>           返回的数据集合泛型
     * @return 返回的已转换为clazz的数据集合
     */
    public <T> List<T> mapToBasicType(Class<T> clazz, List<Map<String, Object>> resultMapList) {
        if (resultMapList != null && resultMapList.size() > 0) {
            List list = new ArrayList();
            for (Map<String, Object> resultMap : resultMapList) {
                for (Map.Entry<String, Object> entry : resultMap.entrySet()) {
                    list.add(getValue(entry.getValue(), clazz));
                }
            }
            return list;
        }
        return null;
    }

    /**
     * list转换
     *
     * @param returnList 要转换的map集合
     * @param clazz      要转换的数据类型
     * @param <T>        返回的数据集合泛型
     * @return 返回的已转换为clazz的数据集合
     */
    private <T> List<T> getlist(List<Map<String, Object>> returnList, Class<T> clazz) {
        List<T> list = new ArrayList<T>();
        //获取实体字段改驼峰式
        Map<String, Field> fieldMap = getField(clazz);
        for (Map<String, Object> map : returnList) {
            //实体类转换
            T t = getObject(map, fieldMap, clazz);
            list.add(t);
        }
        return list;
    }

    /**
     * 实体类转换
     *
     * @param returnMap 原始数据
     * @param fieldMap  转换为驼峰式的字段
     * @param clazz     要转换的数据类型
     * @param <T>       要转换的数据类型泛型
     * @return 转换后的实体类的集合
     */
    private <T> T getObject(Map<String, Object> returnMap, Map<String, Field> fieldMap, Class<T> clazz) {
        try {
            T object = clazz.newInstance();
            for (Map.Entry<String, Object> entry : returnMap.entrySet()) {
                String key = entry.getKey().toUpperCase().replace("_", "");
                Object value = entry.getValue();
                if (value != null) {
                    if (fieldMap.containsKey(key)) {
                        Field field = fieldMap.get(key);
                        Method method = clazz.getDeclaredMethod(setFiledName(field.getName()), field.getType());
                        method.invoke(object, getValue(value, field.getType()));
                    }
                }
            }
            return object;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 字段类型转换
     *
     * @param value 要转换为的数值
     * @param type  数值的类型
     * @return 转换后的数值
     */
    private Object getValue(Object value, Type type) {
        if (type == int.class || type == Integer.class) {
            return StringUtils.toInteger(value);
        } else if (value instanceof Clob) {
            return ClobToString((Clob) value);
        } else if (type == String.class) {
            return StringUtils.toString(value);
        } else if (type == boolean.class) {
            return StringUtils.toBoolean(value);
        } else if (type == Double.class || type == double.class) {
            return StringUtils.toDouble(value);
        } else if (type == long.class || type == Long.class) {
            return StringUtils.toLong(value);
        } else if (value instanceof Date) {
            return DateUtils.parseDate(value);
        } else {
            if (type instanceof Map) {
                Map<String, Field> fieldMap = getField((Class) type);
                return mapToObject(fieldMap, (Class) type, (Map) value);
            }
        }
        return null;
    }

    /**
     * map与实体类字段转换
     *
     * @param fieldMap  转换为驼峰式的字段
     * @param clazz     实体类的类型
     * @param resultMap 原始的字段
     * @param <T>       转换的实体类的泛型
     * @return 转换后的实体类
     */
    private <T> T mapToObject(Map<String, Field> fieldMap, Class<T> clazz, Map<String, Object> resultMap) {
        if (resultMap != null) {
            try {
                T object = clazz.newInstance();
                for (Map.Entry<String, Object> entry : resultMap.entrySet()) {
                    String key = entry.getKey();
                    key = key.toUpperCase().replace("_", "");
                    Object value = entry.getValue();
                    if (null == value) {
                        continue;
                    }
                    if (fieldMap.containsKey(key)) {
                        Field field = fieldMap.get(key);
                        // 得到property对应的setter方法
                        try {
                            Method method = clazz.getMethod(setFiledName(field.getName()), field.getType());
                            method.invoke(object, getValue(value, field.getType()));
                        } catch (Exception e) {
                            logger.error(String.format("%s字段转换失败", key));
                            e.printStackTrace();
                        }
                    }
                }
                return object;
            } catch (InstantiationException e1) {
                e1.printStackTrace();
            } catch (IllegalAccessException e1) {
                e1.printStackTrace();
            }
        }
        return null;
    }

    /**
     * clob与String转换
     *
     * @param clob 要转换为String类型的clob数据
     * @return 转换后的数据
     */
    public String ClobToString(Clob clob) {
        try {
            String reString = "";
            Reader is = clob.getCharacterStream();
            BufferedReader br = new BufferedReader(is);
            String s = br.readLine();
            StringBuffer sb = new StringBuffer();
            while (s != null) {
                sb.append(s);
                s = br.readLine();
            }
            reString = sb.toString();
            if (br != null) {
                br.close();
            }
            if (is != null) {
                is.close();
            }
            return reString;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 通过set赋值
     *
     * @param fieldName 要赋值的字段名
     * @return 带有set的字段
     */
    private String setFiledName(String fieldName) {
        StringBuffer sb = new StringBuffer("set");
        if (!StringUtils.isEmpty(fieldName)) {
            String one = fieldName.substring(0, 1);
            sb.append(one.toUpperCase());
            if (fieldName.length() > 1) {
                sb.append(fieldName.substring(1));
                return sb.toString();
            } else {
                return sb.toString();
            }
        } else {
            return null;
        }
    }

    /**
     * 获取实体字段改驼峰式
     *
     * @param clazz 要更改的实体类
     * @param <T>   返回map的类型泛型
     * @return 原始字段和驼峰式字段的map集合
     */
    private <T> Map<String, Field> getField(Class<T> clazz) {
        Map<String, Field> map = new HashMap<String, Field>();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            String fieldName = field.getName();
            fieldName = fieldName.toUpperCase().replace("_", "");
            map.put(fieldName, field);
        }
        return map;
    }

    /**
     * 实体转map
     *
     * @param sql   要执行的sql
     * @param map   存在sql中的条件信息
     * @param param 条件信息
     */
    protected void getParamMap(String sql, Map<String, Object> map, Object param) {
        Matcher mat = pat.matcher(sql);
        while (mat.find()) {
            String key = mat.group(0).substring(1);
            if (!map.containsKey(key)) {
                Object value = getValue(key, param);
                map.put(key, value);
            }
        }
    }

    /**
     * 实体类通过name取value
     *
     * @param fieldname 字段名称
     * @param param     实体类
     * @return 字段的值
     */
    private Object getValue(String fieldname, Object param) {
        try {
            if (param != null) {
                Method method = param.getClass().getDeclaredMethod(getMethod(fieldname));
                return method.invoke(param);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(String.format("%s类中无属性%s", param.getClass().getName(), fieldname));
        }
        return null;
    }

    /**
     * 获取get的数值
     *
     * @param fieldName 字段名称
     * @return 字段的值
     */
    private String getMethod(String fieldName) {
        StringBuffer stringBuffer = new StringBuffer("get");
        if (!StringUtils.isEmpty(fieldName)) {
            String one = fieldName.substring(0, 1);
            if (!one.equals(fieldName.toUpperCase())) {
                stringBuffer.append(one.toUpperCase());
            } else {
                stringBuffer.append(one);
            }
            if (fieldName.length() > 1) {
                stringBuffer.append(fieldName.substring(1));
            }
            return stringBuffer.toString();
        }
        return null;
    }
}
